use std::cmp::{Ordering, PartialOrd};
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::ops::{Add, Sub};

/// 最小支持的农历公历年。
pub const MIN_YEAR_IN_SOLAR_CALENDAR: u16 = 1901;
/// 最大支持的农历公历年。
pub const MAX_YEAR_IN_SOLAR_CALENDAR: u16 = 2101;
/// 公历月的天数（不包括闰年）
const DAYS_IN_MONTH: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

/// 农历年的新年比公历年晚，这个阵列储存公历年和农历年开始（该年第一天）的偏差量（天数）。此处有公元 1901~2100 年的资料。
///
/// 该数据来源：https://raw.githubusercontent.com/magiclen/chinese-lunisolar-calendar/refs/heads/master/src/lunisolar/constants.rs
pub const NEW_YEAR_DIFFERENCE: [u8; 200] = [
    49, 38, 28, 46, 34, 24, 43, 32, 21, 40, // 1910
    29, 48, 36, 25, 44, 33, 22, 41, 31, 50, // 1920
    38, 27, 46, 35, 23, 43, 32, 22, 40, 29, // 1930
    47, 36, 25, 44, 34, 23, 41, 30, 49, 38, // 1940
    26, 45, 35, 24, 43, 32, 21, 40, 28, 47, // 1950
    36, 26, 44, 33, 23, 42, 30, 48, 38, 27, // 1960
    45, 35, 24, 43, 32, 20, 39, 29, 47, 36, // 1970
    26, 45, 33, 22, 41, 30, 48, 37, 27, 46, // 1980
    35, 24, 43, 32, 50, 39, 28, 47, 36, 26, // 1990
    45, 34, 22, 40, 30, 49, 37, 27, 46, 35, // 2000
    23, 42, 31, 21, 39, 28, 48, 37, 25, 44, // 2010
    33, 22, 40, 30, 49, 38, 27, 46, 35, 24, // 2020
    42, 31, 21, 40, 28, 47, 36, 25, 43, 33, // 2030
    22, 41, 30, 49, 38, 27, 45, 34, 23, 42, // 2040
    31, 21, 40, 29, 47, 36, 25, 44, 32, 22, // 2050
    41, 31, 49, 38, 27, 45, 34, 23, 42, 32, // 2060
    20, 39, 28, 47, 35, 25, 44, 33, 22, 41, // 2070
    30, 49, 37, 26, 45, 35, 23, 42, 32, 21, // 2080
    39, 28, 47, 36, 25, 44, 33, 23, 40, 29, // 2090
    48, 37, 26, 45, 35, 24, 42, 31, 20, 39, // 2100
];

/// 用于小范围日期计算
///
/// 支持公历日期: 0001年 01月 01日  ~ 4095年 12月 31日
///
/// 支持农历公历日期: 1901年 01月 01日 ~ 2101年 01月 29日
///
/// 注意：部分计算并不安全, 结果可能包含: 1582年 10月 5日 - 1582年 10月 14日
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Date {
    /// year: 15 ~ 4, month: 3 ~ 0; data: yyyy_yyyy_yyyy_mmmm
    year_month: u16,
    day: u8,
}

impl Date {
    /// 支持公历日期: 0001年 01月 01日  ~ 4095年 12月 31日
    ///
    /// 支持农历公历日期: 1901年 01月 01日 ~ 2101年 01月 29日
    #[inline(always)]
    pub const fn from_year_month_day(year: u16, month: u8, day: u8) -> Result<Self, DateError> {
        // if year > 5677 || year < 1582 || (year == 1582 && (month < 10 || (month == 10 && day < 15))) {
        if year > 4095 {
            Err(DateError::DateOutOfRange)
        } else if Self::check_date(year, month, day) == false {
            Err(DateError::InvalidDate)
        } else {
            let year_month = (year << 4) + month as u16;
            Ok(Date { year_month, day })
        }
    }
    /// 公历年
    #[inline(always)]
    pub const fn year(&self) -> u16 {
        self.year_month >> 4
    }
    /// 公历月
    #[inline(always)]
    pub const fn month(&self) -> u8 {
        (self.year_month & 0xF) as u8
    }
    /// 公历日
    #[inline(always)]
    pub const fn day(&self) -> u8 {
        self.day
    }
}

impl PartialOrd for Date {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Date {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.year_month.cmp(&other.year_month) {
            Ordering::Equal => self.day.cmp(&other.day),
            other => other,
        }
    }
}

impl Add<u32> for Date {
    type Output = Date;
    /// 不安全计算, 结果可能包含: 1582年 10月 5日 - 1582年 10月 14日
    fn add(self, days: u32) -> Date {
        let mut year = self.year();
        let mut month = self.month();
        let mut day = self.day as u32 + days;
        // const DAYS_IN_MONTH: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        while day > DAYS_IN_MONTH[month as usize - 1] as u32 {
            day -= DAYS_IN_MONTH[month as usize - 1] as u32;
            if month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
                day -= 1;
            }
            month += 1;
            if month > 12 {
                month = 1;
                year += 1;
            }
        }

        Date::from_year_month_day(year, month, day as u8).unwrap()
    }
}
/// 112313
impl Sub for Date {
    type Output = i32;
    /// 不安全计算, 结果可能包含: 1582年 10月 5日 - 1582年 10月 14日
    fn sub(self, other: Date) -> i32 {
        Self::calc_date_duration(&self, &other) * -1
    }
}

impl Sub<u32> for Date {
    type Output = Date;
    /// 不安全计算, 结果可能包含: 1582年 10月 5日 - 1582年 10月 14日
    fn sub(self, days: u32) -> Date {
        let mut year = self.year();
        let mut month = self.month();
        let mut day = self.day as i32 - days as i32;
        // const DAYS_IN_MONTH: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        while day < 1 {
            month -= 1;
            if month < 1 {
                month = 12;
                year -= 1;
            }
            day += DAYS_IN_MONTH[month as usize - 1] as i32;
            if month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
                day += 1;
            }
        }
        Date::from_year_month_day(year, month, day as u8).unwrap()
    }
}

impl Date {
    /// 判断是否是闰年
    #[inline(always)]
    pub const fn is_leap_year(&self) -> bool {
        Self::check_leap_year(self.year_month >> 4)
    }

    #[inline(always)]
    const fn check_leap_year(year: u16) -> bool {
        (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
    }

    #[inline(always)]
    const fn check_date(year: u16, month: u8, day: u8) -> bool {
        match month {
            1 | 3 | 5 | 7 | 8 | 10 | 12 if day <= 31 => true,
            4 | 6 | 9 | 11 if day <= 30 => true,
            2 => match (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
                true if day <= 29 => true,
                false if day <= 28 => true,
                _ => false,
            },
            _ => false,
        }
    }

    /// 计算两个日期之间相隔的天数
    #[inline(always)]
    fn calc_date_duration(origin_date: &Date, target_date: &Date) -> i32 {
        let (start, end, res_wi);
        if origin_date < target_date {
            start = origin_date;
            end = target_date;
            res_wi = 1;
        } else {
            start = target_date;
            end = origin_date;
            res_wi = -1;
        }
        let mut days: i32 = 0;
        let year_wi;
        if start.month() < 2 {
            year_wi = 0;
        } else {
            year_wi = 1;
        }
        for year in start.year() + year_wi..end.year() + year_wi {
            days += 365;
            if Self::check_leap_year(year) {
                days += 1;
            }
        }
        // const DAYS_IN_MONTH: [u8; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        let (months, month_wi);
        if start.month() < end.month() {
            months = start.month()..end.month();
            month_wi = 1;
        } else {
            months = end.month()..start.month();
            month_wi = -1;
        }
        for month in months {
            days += month_wi * DAYS_IN_MONTH[month as usize - 1] as i32;
            if month == 2 && Self::check_leap_year(end.year()) {
                days += month_wi;
            }
        }
        res_wi * (days + end.day as i32 - start.day as i32)
    }

    #[inline(always)]
    const fn calc_new_year_date(year: u16) -> Result<Self, DateError> {
        if year < MIN_YEAR_IN_SOLAR_CALENDAR || year > MAX_YEAR_IN_SOLAR_CALENDAR {
            return Err(DateError::DateOutOfRange);
        }
        if year == MAX_YEAR_IN_SOLAR_CALENDAR {
            return Self::from_year_month_day(year, 1, 29);
            // let year_month = (self.year() << 4) + 1;
            // return Ok( Date { year_month, day: 29 } )
        }
        let new_year_diff =
            NEW_YEAR_DIFFERENCE[(year - MIN_YEAR_IN_SOLAR_CALENDAR) as usize] as u16;
        // let year_month = (self.year() << 4) + 1 + new_year_diff / 31;
        // Ok( Date { year_month, day: (new_year_diff % 31 + 1) as u8 } )
        Self::from_year_month_day(
            year,
            (1 + new_year_diff / 31) as u8,
            (new_year_diff % 31 + 1) as u8,
        )
    }

    #[inline(always)]
    pub fn weekday(&self) -> u8 {
        let year = self.year();
        let month = self.month();
        let day = self.day();
        const GREGORIAN: Result<Date, DateError> = Date::from_year_month_day(1582, 10, 15);
        // 判断日期是否在格里历之后（1582年10月15日）
        if *self >= GREGORIAN.unwrap() {
            Self::gregorian_weekday(year, month, day)
        } else {
            Self::julian_weekday(year, month, day)
        }
    }

    #[inline(always)]
    const fn gregorian_weekday(mut year: u16, mut month: u8, day: u8) -> u8 {
        if month <= 2 {
            month += 12;
            year -= 1;
        }
        let (year, month, day) = (year as i32, month as i32, day as i32);
        let h =
            (day + 2 * month + 3 * (month + 1) / 5 + year + year / 4 - year / 100 + year / 400) % 7;
        ((h + 7) % 7) as u8
    }

    #[inline(always)]
    const fn julian_weekday(mut year: u16, mut month: u8, day: u8) -> u8 {
        if month <= 2 {
            month += 12;
            year -= 1;
        }
        let (year, month, day) = (year as i32, month as i32, day as i32);
        let h = (day + 5 + (13 * (month + 1) / 5) + year + (year / 4)) % 7;
        ((h + 7) % 7) as u8
    }
}

impl Date {
    /// 获取当前日期所在公历年中国农历新年的公历日期
    /// 支持公历年: 1901 ~ 2101
    #[inline(always)]
    pub const fn get_new_year_date(&self) -> Result<Self, DateError> {
        Self::calc_new_year_date(self.year())
    }

    /// 获取当前日期所在公历年的下一个中国农历新年的公历日期
    /// 支持公历年: 1901 ~ 2101
    #[inline(always)]
    pub const fn get_next_new_year_date(&self) -> Result<Self, DateError> {
        Self::calc_new_year_date(self.year() + 1)
    }

    /// 获取当前日期距离下一个中国农历新年公历日期剩余的天数
    #[inline(always)]
    pub fn get_remaining_days_until_the_next_new_year(&self) -> Result<u16, DateError> {
        let new_year = self.get_new_year_date()?;
        if *self < new_year {
            Ok(Self::calc_date_duration(self, &new_year) as u16 - 1)
        } else {
            let next_new_year = self.get_next_new_year_date()?;
            Ok(Self::calc_date_duration(self, &next_new_year) as u16 - 1)
        }
    }

    /// 获取当前日期距离年末剩余的天数
    #[inline(always)]
    pub fn get_remaining_days_the_year(&self) -> Result<u16, DateError> {
        let end_of_the_year = Date::from_year_month_day(self.year(), 12, 31)?;
        Ok(Self::calc_date_duration(self, &end_of_the_year) as u16)
    }

    /// 获取当前日期所在公历年第一周周一的日期
    #[inline(always)]
    pub fn get_first_week_date(&self) -> Result<Self, DateError> {
        let first_day = Self::from_year_month_day(self.year(), 1, 4)?;
        Ok(first_day - first_day.weekday() as u32)
    }

    /// 获取当前日期为所在公历年的周数
    #[inline(always)]
    pub fn get_week_num_the_year(&self) -> Result<u32, DateError> {
        if *self >= Self::from_year_month_day(self.year() + 1, 1, 4)?.get_first_week_date()? {
            Ok(1)
        } else {
            Ok((*self - self.get_first_week_date()?) as u32 / 7 + 1)
        }
    }
}

#[derive(Debug, Eq, PartialEq)]
pub enum DateError {
    InvalidDate,
    DateOutOfRange,
}

impl Display for DateError {
    #[inline(always)]
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidDate => Display::fmt("Invalid date", f),
            Self::DateOutOfRange => Display::fmt("Date out of range", f),
        }
    }
}

impl Error for DateError {}
